package app

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego/logs"

	"cvevulner/cve-ddd/domain"
	"cvevulner/cve-ddd/domain/bulletin"
	"cvevulner/cve-ddd/domain/obs"
	"cvevulner/cve-ddd/domain/repository"
	"cvevulner/cve-ddd/domain/updateinfo"
	"cvevulner/models"
	"cvevulner/util"
)

const (
	indexFileName       = "index.txt"
	updateFixedFileName = "update_fixed.txt"
)

type HotPatchService interface {
	GenerateBulletins([]CmdToGenerateBulletins) error
}

func NewHotPatchService(
	r repository.CveRepository,
	b bulletin.Bulletin,
	o obs.OBS,
	u updateinfo.UpdateInfo,
) *hotPatchService {
	return &hotPatchService{
		repository: r,
		bulletin:   b,
		obs:        o,
		updateInfo: u,
	}
}

type hotPatchService struct {
	repository repository.CveRepository
	bulletin   bulletin.Bulletin
	obs        obs.OBS
	updateInfo updateinfo.UpdateInfo
}

func (h *hotPatchService) GenerateBulletins(cmds []CmdToGenerateBulletins) error {
	handleBranch := []string{
		"openEuler-20.03-LTS-SP4",
		"openEuler-22.03-LTS-SP1",
		"openEuler-22.03-LTS-SP3",
		"openEuler-24.03-LTS",
	}

	domain.InitMaintainVersion(handleBranch)

	var cvesForUpdateInfo domain.Cves
	var uploadFileName []string
	id, err := h.generateBulletinId()
	if err != nil {
		return err
	}

	for _, cmd := range cmds {
		if exist := h.repository.IssueNumExist(cmd.HotIssueNum); exist {
			continue
		}

		cves, err := h.repository.FindCves(
			repository.Option{
				CveNum:    cmd.CveNum,
				Component: cmd.Component,
			})
		if err != nil {
			logs.Error("find cve %s, error %s", cmd.CveNum, err.Error())
			return err
		}
		if len(cves) == 0 {
			logs.Error("find cve %s nil", cmd.CveNum)
			continue
		}
		// all cves have the same hot issue number
		for k := range cves {
			cves[k].HotIssueNum = cmd.HotIssueNum
			cves[k].AffectedVersion = []string{cmd.Branch}
			cves[k].Component = cmd.Component
		}

		bulletins := cves.GenerateBulletins()
		for _, b := range bulletins {
			b.PatchUrl = cmd.PatchUrl

			id++
			b.Identification = fmt.Sprintf("openEuler-HotPatchSA-%d-%d", util.Year(), id)

			xmlData, err := h.bulletin.GenerateHotPatch(&b)
			if err != nil {
				logs.Error("component: %s, to xml error: %s", b.Component, err.Error())

				continue
			}

			fileName := fmt.Sprintf("cvrf-%s.xml", b.Identification)
			if err := h.obs.UploadToDynamicDir(fileName, xmlData); err != nil {
				logs.Error("component: %s, upload to obs error: %s", b.Component, err.Error())

				continue
			}

			uploadFileName = append(uploadFileName, fileName)

			cvesForUpdateInfo = append(cvesForUpdateInfo, b.Cves...)
		}

		if err := h.repository.SaveIssueNum(cmd.HotIssueNum); err != nil {
			logs.Error("save issue num %s error %s", cmd.HotIssueNum, err.Error())
		}
	}

	h.appendHotPatchToFiles(uploadFileName)

	return h.uploadUpdateInfo(cvesForUpdateInfo)
}

func (h *hotPatchService) generateBulletinId() (int, error) {
	maxID, err := h.repository.MaxBulletinID()
	if err != nil {
		return 0, err
	}

	maxID = strings.TrimSpace(maxID)
	if maxID == "" {
		return 1000, nil
	}

	thisYear := util.Year()
	split := strings.Split(strings.Trim(maxID, ".xml"), "-")
	if split[3] != strconv.Itoa(thisYear) {
		return 1000, nil
	}

	return strconv.Atoi(split[4])
}

func (h *hotPatchService) uploadUpdateInfo(cves domain.Cves) error {
	if len(cves) == 0 {
		return nil
	}

	for version, v := range cves.GroupByVersion() {
		bytes, err := h.updateInfo.Generate(v)
		if err != nil {
			logs.Error("generate updateinfo of %s error %s", version, err.Error())
			continue
		}

		fileName := fmt.Sprintf("%s_updateinfo.xlsx", version)
		if err := h.obs.UploadUpdateInfo(fileName, bytes); err != nil {
			logs.Error("version: %s, upload to obs error: %s", version, err.Error())

			continue
		}
	}

	return nil
}

func (h *hotPatchService) appendHotPatchToFiles(files []string) {
	if len(files) == 0 {
		return
	}

	var appendContent string
	for _, v := range files {
		appendContent += fmt.Sprintf("\n%d/%s", time.Now().Year(), v)
	}

	h.updateFileByContent(updateFixedFileName, appendContent)

	var oldContent string
	oldRecords := models.GetCvrfAllFile(models.SaFileRecordHotPatch)
	for _, v := range oldRecords {
		oldContent += fmt.Sprintf("\n%d/%s", time.Now().Year(), v.FileName)
	}

	h.updateFileByContent(indexFileName, oldContent+appendContent)
}

func (h *hotPatchService) updateFileByContent(file, newContent string) {
	oldContent, err := h.obs.DownloadFromDynamicDir(file)
	if err != nil {
		logs.Error("download %s error: %s", file, err.Error())

		return
	}

	oldContent = append(oldContent, newContent...)

	if err = h.obs.UploadToDynamicDir(file, oldContent); err != nil {
		logs.Error("upload %s error: %s", file, err.Error())
	}
}
